﻿// ------------------------------------------------------------------------
// 项目名称：Canroc.Net 
// 版权归属：Canroc（https://gitee.com/canroc）
//
// 许可证信息
// Canroc.Net项目主要遵循 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-APACHE 文件。
//
// 使用条款：
// 1.使用本项目应遵守相关法律法规和许可证的要求。
// 2.不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动。
// 3.任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任
//
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
//
// 其他重要信息
// Canroc.Net 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// ------------------------------------------------------------------------

using Canroc.Net.Application.AuthenticationService.Dto;
using Canroc.Net.Application.LogByAuthService;
using Canroc.Net.Application.LogByAuthService.Dto;
using Canroc.Net.Application.SysRoleService;
using Canroc.Net.Application.SysUserService;
using Canroc.Net.SystemService.RedisService;
using Lazy.Captcha.Core;

// ReSharper disable once CheckNamespace
namespace Canroc.Net.Application.AuthenticationService;

/// <summary>
///     鉴权应用服务
/// </summary>
[ApiDescriptionSettings("Default")]
[NoSign]
public partial class AuthenticationAppService(IServiceProvider serviceProvider) : IDynamicApiController
{
    /// <summary>
    ///     后台系统登录
    /// </summary>
    /// <returns></returns>
    [AllowAnonymous]
    public async Task<LoginOutput> LoginAsync(LoginInput input)
    {
        var captcha = serviceProvider.GetRequiredService<ICaptcha>();
        var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();

        // 验证验证码
        if (!captcha.Validate(input.CodeId, input.Code))
        {
            throw Oops.Oh(ErrorCodeEnum.CaptchaError);
        }

        // 获取加密后的密码
        var secretKeyOption = App.GetOptions<SecretKeyOption>();
        string realPassword;
        try
        {
            realPassword = input.PassWord.Sm2Decrypt(secretKeyOption.Sm2PrivateKey);
        }
        catch (Exception ex)
        {
            throw Oops.Oh(ErrorCodeEnum.PasswordError, ex.Message);
        }

        // 查询用户信息
        var sysUserAppService = serviceProvider.GetRequiredService<SysUserAppService>();
        var user = await sysUserAppService.GetDataAsync(input.Account);
        _ = user ?? throw Oops.Oh(ErrorCodeEnum.UnRegister);

        // 判断账号是否被锁定
        if (user.ErrorNum >= 3)
        {
            throw Oops.Oh(ErrorCodeEnum.AccountLock);
        }

        // 判断账号密码是否正确
        var identical = realPassword.Sm2VerifySign(secretKeyOption.Sm2PublicKey, user.PassWord, user.Account);

        // 更新登录信息
        var authenticationLogService = serviceProvider.GetRequiredService<LogByAuthAppService>();
        _ = await authenticationLogService.AddAsync(new AuthLogInput
        {
            Account = input.Account,
            InputValue = input.PassWord,
            Success = identical,
            Ip = httpContextAccessor.HttpContext!.GetRemoteIpAddressToIPv4(true)
        });

        if (!identical)
        {
            // 增加用户输错次数
            _ = await sysUserAppService.EditErrorNum(user.Id, user.ErrorNum + 1);
            throw Oops.Oh(ErrorCodeEnum.PasswordError);
        }

        // 验证账号是否被冻结
        if (!user.IsEnable)
        {
            throw Oops.Oh(ErrorCodeEnum.AccountFreeze);
        }

        // 生成Token令牌
        var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
        {
            { ClaimConst.ClaimSource, "PC" },
            { ClaimConst.ClaimAccount, user.Account },
            { ClaimConst.ClaimName, user.Name },
            { ClaimConst.ClaimRoles, user.RoleCodes },
            { ClaimConst.ClaimSupper, user.IsAdmin ? "TRUE" : "FALSE" }
        });

        // 将输错次数恢复为0
        _ = await sysUserAppService.EditErrorNum(user.Id, 0);

        // 查询用户的角色信息
        var sysRoleAppService = serviceProvider.GetRequiredService<SysRoleAppService>();
        var userRoles = await sysRoleAppService.GetRoleResourceAsync(user.RoleIds);

        // 异步插入Redis的登录缓存中
        var session = new SessionRedisModel { UserId = user.Id, AccessToken = accessToken, Routes = userRoles };

        // 插入Redis的登录缓存中
        var redisService = serviceProvider.GetRequiredService<IRedisService>();
        _ = redisService.HashSetAsync("session", user.Account, session);

        return new LoginOutput
        {
            AccessToken = accessToken, RefreshToken = JWTEncryption.GenerateRefreshToken(accessToken)
        };
    }

    /// <summary>
    ///     退出
    /// </summary>
    /// <returns></returns>
    public async Task LogoutAsync()
    {
        // 清除Redis中的Session信息
        var redisService = serviceProvider.GetRequiredService<IRedisService>();
        _ = await redisService.HashDeleteAsync("session", UserManager.Account);
    }

    /// <summary>
    ///     swagger登录检查
    /// </summary>
    /// <returns></returns>
    [AllowAnonymous]
    [ApiDescriptionSettings(false)]
    [Route("check-url")]
    [NonUnify]
    public async Task<int> SwaggerCheckUrl()
    {
        var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
        var token = httpContextAccessor.HttpContext?.Request.Headers.Authorization.ToString();
        const string tokenFixedBeginning = "Bearer ";
        var prefixLenght = tokenFixedBeginning.Length;
        if (token is not null && token.StartsWith(tokenFixedBeginning) &&
            JWTEncryption.Validate(token[prefixLenght..]).IsValid)
        {
            return await Task.FromResult(200);
        }

        // 清空浏览器缓存
        App.HttpContext.SignoutToSwagger();
        return await Task.FromResult(401);
    }

    /// <summary>
    ///     swagger登录提交
    /// </summary>
    /// <param name="auth"></param>
    /// <returns></returns>
    [AllowAnonymous]
    [ApiDescriptionSettings(false)]
    [Route("submit-url")]
    [NonUnify]
    public async Task<int> SwaggerSubmitUrl([FromForm] SpecificationAuth auth)
    {
        // 查询用户信息
        var sysUserAppService = serviceProvider.GetRequiredService<SysUserAppService>();
        var user = await sysUserAppService.GetDataAsync(auth.UserName);
        if (user is null)
        {
            return 401;
        }

        // 密码错误
        var secretKeyOption = App.GetOptions<SecretKeyOption>();
        if (!auth.Password.Sm2VerifySign(secretKeyOption.Sm2PublicKey, user.PassWord, user.Account))
        {
            return 401;
        }

        // 验证账号是否被冻结
        if (!user.IsEnable)
        {
            return 401;
        }

        // 验证账号是否超级管理员
        if (!user.IsAdmin)
        {
            return 401;
        }

        // 生成Token令牌
        var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
        {
            { ClaimConst.ClaimSource, "Swagger" },
            { ClaimConst.ClaimAccount, user.Account },
            { ClaimConst.ClaimName, user.Name },
            { ClaimConst.ClaimRoles, user.RoleCodes },
            { ClaimConst.ClaimSupper, "TRUE" }
        });

        var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
        // 设置Swagger自动登录
        httpContextAccessor.HttpContext!.SigninToSwagger(accessToken);
        // 设置刷新Token令牌
        httpContextAccessor.HttpContext!.Response.Headers["x-access-token"] =
            JWTEncryption.GenerateRefreshToken(accessToken);

        // 异步插入Redis的登录缓存中
        var redisService = serviceProvider.GetRequiredService<IRedisService>();
        _ = redisService.HashSetAsync("session", user.Account,
            new SessionRedisModel { UserId = user.Id, AccessToken = accessToken });

        return 200;
    }

    /// <summary>
    ///     生成验证码
    /// </summary>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet("code")]
    [SuppressMonitor]
    public CodeOutput GenerateCode()
    {
        var captcha = serviceProvider.GetRequiredService<ICaptcha>();
        var captchaId = Guid.NewGuid().ToString().Replace("-", "");
        var captchaData = captcha.Generate(captchaId);

        return new CodeOutput { Id = captchaId, Base64 = captchaData.Base64 };
    }
}